Stroking and Filling Graphics Primitives

The JavaTM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form

Trail: 2D Graphics
Lesson: Displaying Graphics with Graphics2D

## Stroking and Filling Graphics Primitives

> By changing the stroke and paint attributes in the `Graphics2D` context before rendering, you can easily apply fancy line styles and fill patterns to graphics primitives. For example, you can draw a dashed line by creating an appropriate `Stroke` object and calling `setStroke` to add it to the `Graphics2D` context before you render the line. Similarly, you can apply a gradient fill to a `Shape` by creating a `GradientPaint` object and adding it to the `Graphics2D` context by calling `setPaint` before you render the `Shape`.

> The following applet demonstrates how you can render basic geometries by using the `Graphics2D` `draw` and `fill` methods.

> This is a picture of the applet's GUI. To run the applet, click the picture. The applet will appear in a new browser window.
> `ShapesDemo2D.java` contains the complete code for this applet.

> Each of the shapes drawn by the applet is constructed from one of the geometries and is then rendered through `Graphics2D`. The `rectHeight` and `rectWidth` variables in this example define the dimensions of the space where each shape is drawn, in pixels. The x and y variables change for each shape so that they are drawn in a grid formation.

>     // draw Line2D.Double
>     g2.draw(new Line2D.Double(x, y+rectHeight-1,
>                               x + rectWidth, y));

>     // draw Rectangle2D.Double
>     g2.setStroke(stroke);
>     g2.draw(new Rectangle2D.Double(x, y,
>                                    rectWidth,
>                                    rectHeight));

>     // draw RoundRectangle2D.Double
>     g2.setStroke(dashed);
>     g2.draw(new RoundRectangle2D.Double(x, y,
>                                        rectWidth,
>                                        rectHeight,
>                                        10, 10));

>     // draw Arc2D.Double
>     g2.setStroke(wideStroke);
>     g2.draw(new Arc2D.Double(x, y,
>                              rectWidth,
>                              rectHeight,
>                              90, 135,
>                              Arc2D.OPEN));

>     // draw Ellipse2D.Double
>     g2.setStroke(stroke);
>     g2.draw(new Ellipse2D.Double(x, y,
>                                  rectWidth,
>                                  rectHeight));

>     // draw GeneralPath (polygon)
>     int x1Points[] = {x, x+rectWidth,
>                       x, x+rectWidth};
>     int y1Points[] = {y, y+rectHeight,
>                       y+rectHeight, y};
>     GeneralPath polygon = new
>     	 GeneralPath(GeneralPath.WIND_EVEN_ODD,
>     		     x1Points.length);
>     polygon.moveTo(x1Points[0], y1Points[0]);

>     for (int index = 1;
>          index < x1Points.length;
>          index++) {
>             polygon.lineTo(x1Points[index],
>                            y1Points[index]);
>     };

>     polygon.closePath();
>     g2.draw(polygon);

>     // draw GeneralPath (polyline)
>     int x2Points[] = {x, x+rectWidth, x,
>                       x+rectWidth};
>     int y2Points[] = {y, y+rectHeight,
>                       y+rectHeight, y};
>     GeneralPath polyline = new
>              GeneralPath(GeneralPath.WIND_EVEN_ODD,
>                	     x2Points.length);

>     polyline.moveTo (x2Points[0], y2Points[0]);

>     for (int index = 1;
>          index < x2Points.length;
>          index++) {
>      	 polyline.lineTo(x2Points[index],
>              y2Points[index]);
>     };

>     g2.draw(polyline);

>     // fill Rectangle2D.Double (red)
>     g2.setPaint(red);
>     g2.fill(new Rectangle2D.Double(x, y,
>             rectWidth, rectHeight));

>     // fill RoundRectangle2D.Double
>     g2.setPaint(redtowhite);
>     g2.fill(new RoundRectangle2D.Double(x, y,
>                                        rectWidth,
>                                        rectHeight,
>                                        10, 10));

>     // fill Arc2D
>     g2.setPaint(red);
>     g2.fill(new Arc2D.Double(x, y, rectWidth,
>                              rectHeight, 90,
>                              135, Arc2D.OPEN));

>     // fill Ellipse2D.Double
>     g2.setPaint(redtowhite);
>     g2.fill (new Ellipse2D.Double(x, y,
>                                   rectWidth,
>                                   rectHeight));

>     // fill and stroke GeneralPath
>     int x3Points[] = {x, x+rectWidth, x,
>                       x+rectWidth};
>     int y3Points[] = {y, y+rectHeight,
>                       y+rectHeight, y};

>     GeneralPath filledPolygon = new
>              GeneralPath(GeneralPath.WIND_EVEN_ODD,
>     		     x3Points.length);
>     filledPolygon.moveTo(x3Points[0],
>                          y3Points[0]);

>     for (int index = 1;
>          index < x3Points.length;
>          index++) 	{
>     	filledPolygon.lineTo(x3Points[index],
>                                  y3Points[index]);

>     };
>     filledPolygon.closePath();
>     g2.setPaint(red);
>     g2.fill(filledPolygon);

> Note that this example uses the double-precision implementations of the geometries classes. Where applicable, float and double-precision implementations of each of the geometries are provided as inner classes.

### Drawing Curves

> The `Cubic` and `Quad` applets demonstrate how to create cubic and quadratic curves using `CubicCurve2D` and `QuadCurve2D` respectively. These applets also demonstrate how the curves are drawn with respect to the positioning of the control points by allowing you to interactively move both the control points and the end points.

> ### Example: Quad

> > The `Quad` applet demonstrates a quadratic curve, which is a curved segment that has two endpoints and only one control point. The control point determines the shape of the curve by controlling both of the endpoint tangent vectors.

> > This is a picture of the applet's GUI. To run the applet, click the picture. The applet will appear in a new browser window.
> > `Quad.java` contains the complete code for this applet.

> > First, a new quadratic curve is created with two endpoints and a control point and the locations of the points are set with respect to the size of the window.

> > >     QuadCurve2D.Double quad = new QuadCurve2D.Double();

> > >     Point2D.Double start, end, control;
> > >     start = new Point2D.Double();
> > >     one = new Point2D.Double();
> > >     control = new Point2D.Double();

> > >     quad.setCurve(start, one, control);

> > >     start.setLocation(w/2-50, h/2);
> > >     end.setLocation(w/2+50, h/2);
> > >     control.setLocation((int)(start.x)+50, (int)(start.y)-50);

> > Every time the user moves one of the points, the curve is reset.

> > >     quad.setCurve(start, control, end);

> ### Example: Cubic

> > The `Cubic` sample demonstrates a cubic curve, which is a curved segment that has two endpoints and two control points. Each control point determines the shape of the curve by controlling one of the endpoint tangent vectors. In the `Cubic` sample, colored squares are drawn where the control points and endpoints are located. The blue control point controls the tangent vector of the red endpoint and the green control point controls the tangent vector of the magenta endpoint.

> > This is a picture of the applet's GUI. To run the applet, click the picture. The applet will appear in a new browser window.
> > `Cubic.java` contains the complete code for this applet.

> > A new cubic curve is created with two endpoints and a two control points and the locations of the points are set with respect to the size of the window.

> > >     CubicCurve2D.Double cubic = new CubicCurve2D.Double();

> > >     Point2D.Double start, end, one, two;
> > >     start = new Point2D.Double();
> > >     one = new Point2D.Double();
> > >     two = new Point2D.Double();
> > >     end = new Point2D.Double();

> > >     cubic.setCurve(start, one, two, end);

> > >     ...

> > >     start.setLocation(w/2-50, h/2);
> > >     end.setLocation(w/2+50, h/2);
> > >     one.setLocation((int)(start.x)+25, (int)(start.y)-25);
> > >     two.setLocation((int)(end.x)-25, (int)(end.y)+25);

> > As in the `Quad` example, the curve is reset every time the points are moved.

> > >     cubic.setCurve(start, one, two, end);

### Drawing Arbitrary Shapes

> The `ShapesDemo` example uses `GeneralPath` to make the hourglass-shaped polygons, but you can also use `GeneralPath` to make arbitrary shapes with both straight and curved lines.

> ### Example: Odd_Shape

> > The `Odd_Shape` sample uses `GeneralPath` to create the arbitrary shape shown in the Shapes section.

> > This is a picture of the applet's GUI. To run the applet, click the picture. The applet will appear in a new browser window.
> > `Odd_Shape.java` contains the complete code for this applet.

> > The following code creates a new `GeneralPath` and adds the first point to the path.

> > >     GeneralPath oddShape = new GeneralPath();
> > >     ...

> > >     x = w/2 + 50;
> > >     y = h/2 - 25;

> > >     x2 = x;
> > >     y2 = y;

> > >     oddShape.moveTo(x, y);

> > After the first point is added to the path, three straight lines are added to the path.

> > >     x -= 100;
> > >     oddShape.lineTo(x, y);
> > >     y += 50;
> > >     oddShape.lineTo(x, y);
> > >     x += 100;
> > >     oddShape.lineTo(x, y);

> > Finally, a cubic curve is added to the path.

> > >     x += 10;
> > >     y -= 10;
> > >     x1 = x - 20;
> > >     y1 = y - 20;
> > >     oddShape.curveTo(x, y, x1, y1, x2, y2);

### Defining Fancy Line Styles and Fill Patterns

> You probably noticed that in the previous example some of the shapes have thicker outlines or are filled with a two-color gradient. Using the Java 2D `Stroke` and `Paint` classes, you can easily define fancy line styles and fill patterns.

> ### Line Styles

> > Line styles are defined by the stroke attribute in the `Graphics2D` rendering context. To set the stroke attribute, you create a `BasicStroke` object and pass it into the `Graphics2D` `setStroke` method.

> > A `BasicStroke` object holds information about the line width, join style, end-cap style, and dash style. This information is used when a `Shape` is rendered with the `draw` method.

> > The line width is the thickness of the line measured perpendicular to its trajectory. The line width is specified as a `float` value in user coordinate units, which are roughly equivalent to 1/72 inch when the default transform is used.

> > The join style is the decoration that is applied where two line segments meet. `BasicStroke` supports three join styles:

> > `JOIN_BEVEL`

> > `JOIN_MITER`

> > `JOIN_ROUND`

> > The end-cap style is the decoration that is applied where a line segment ends. `BasicStroke` supports three end-cap styles:

> > `CAP_BUTT`

> > `CAP_ROUND`

> > `CAP_SQUARE`

> > The dash style defines the pattern of opaque and transparent sections applied along the length of the line. The dash style is defined by a dash array and a dash phase. The dash array defines the dash pattern. Alternating elements in the array represent the dash length and the length of the space between dashes in user coordinate units. Element 0 represents the first dash, element 1 the first space, and so on. The dash phase is an offset into the dash pattern, also specified in user coordinate units. The dash phase indicates what part of the dash pattern is applied to the beginning of the line.

> ### Fill Patterns

> > Fill patterns are defined by the paint attribute in the `Graphics2D` rendering context. To set the paint attribute, you create an instance of an object that implements the `Paint` interface and pass it into the `Graphics2D` `setPaint` method.

> > Three classes implement the `Paint` interface: `Color`, `GradientPaint`, and `TexturePaint`. `GradientPaint` and `TexturePaint` are new in JDK 1.2.

> > To create a GradientPaint, you specify a beginning position and color and an ending position and color. The gradient changes proportionally from one color to the other along the line connecting the two positions.

> > The pattern for a `TexturePaint` is defined by a `BufferedImage`. To create a `TexturePaint`, you specify the image that contains the pattern and a rectangle that is used to replicate and anchor the pattern.

> ### Example: StrokeAndFill

> > The `StrokeAndFill` applet allows the user to select a graphics primitive, a line style, and a paint style and to either stroke the object's outline, fill it with the selected paint, or stroke the object in black and then fill it with the selected paint.

> > This is a picture of the applet's GUI. To run the applet, click the picture. The applet will appear in a new browser window.

> > `StrokeAndFill.java` contains the complete code for this applet.

> > The primitives are initialized and entered into an array of `Shape` objects. The following code creates a `Rectangle` and an `Ellipse2D.Double` and enters them into the `shapes` array.

> > >     shapes[0] = new Rectangle(0, 0, 100, 100);
> > >     shapes[1] = new Ellipse2D.Double(0.0, 0.0, 100.0, 100.0);

> > To create a `Shape` object from a text string, you must first create a `TextLayout` object from the text string.

> > >     TextLayout textTl = new TextLayout("Text",
> > >     	  new Font("Helvetica", 1, 96),
> > >     	  new FontRenderContext(null, false, false));

> > The following lines transform the `TextLayout` so that it is centered on the origin and then enter the `Shape` object resulting from the call to `getOutline` into the `shapes` array.

> > >     AffineTransform textAt = new AffineTransform();
> > >     textAt.translate(0,
> > >         (float)textTl.getBounds().getHeight());
> > >     shapes[2] = textTl.getOutline(textAt);

> > You can choose a primitive by accessing the appropriate index into the `shapes` array.

> > >     Shape shape =
> > >         shapes[Transform.primitive.getSelectedIndex()];

> > How rendering is performed depends on which rendering option is chosen.

> > - When the user chooses stroke, `Graphics2D.draw` is called to perform the rendering. If text is chosen as the primitive, the glyph outlines are retrieved and then rendered with the `draw` method.
> > - When the user chooses fill, `Graphics2D.fill` or `Graphics2D.drawString` is called to perform the rendering.
> > - When the user chooses stroke and fill, `fill` or `drawString` is called to fill the `Shape`, and then `draw` is called to stroke its outline.

> > > Note: To both fill and stroke a graphics primitive, you need to make two separate method calls: `fill` or `drawString` to fill its interior and `draw` to stroke its outline.

> > The three line styles used in this example--thin, thick, and dashed--are instances of `BasicStroke`.

> > >     // Sets the Stroke.
> > >     ...
> > >     case 0 : g2.setStroke(new BasicStroke(3.0f)); break;
> > >     case 1 : g2.setStroke(new BasicStroke(8.0f)); break;
> > >     case 2 : float dash[] = {10.0f};
> > >              g2.setStroke(new BasicStroke(3.0f,
> > >                  BasicStroke.CAP_BUTT,
> > >                  BasicStroke.JOIN_MITER,
> > >                  10.0f, dash, 0.0f));
> > >              break;

> > The dash style in this example has 10 unit dashes alternating with 10 unit spaces. The beginning of the dash pattern is applied to the beginning of the line--the dash phase is set to 0.0.

> > Three paint styles are used in this example--solid, gradient, and polka. The solid-color paint style is an instance of `Color`, the gradient an instance of `GradientPaint`, and the pattern an instance of `TexturePaint`.

> > >     // Sets the Paint.
> > >     ...
> > >     case 0 : g2.setPaint(Color.blue); break;
> > >     case 1 : g2.setPaint(new GradientPaint(0, 0,
> > >                               Color.lightGray,
> > >                               w-250, h, Color.blue, false));
> > >              break;
> > >     case 2 : BufferedImage bi = new BufferedImage(5, 5,
> > >                                     BufferedImage.TYPE_INT_RGB);
> > >              Graphics2D big = bi.createGraphics();
> > >              big.setColor(Color.blue);
> > >              big.fillRect(0, 0, 5, 5);
> > >              big.setColor(Color.lightGray);
> > >              big.fillOval(0, 0, 5, 5);
> > >              Rectangle r = new Rectangle(0,0,5,5);
> > >              g2.setPaint(new TexturePaint(bi, r));
> > >              break;

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form

Copyright 1995-2005 Sun Microsystems, Inc. All rights reserved.